using System;

namespace LaplaceMeshDeformer
{
    /// <summary>
    /// 三维向量类
    /// </summary>
    public class Vector3D
    {
        private double x;
        private double y;
        private double z;

        /// <summary>
        /// X坐标
        /// </summary>
        public double X
        {
            get { return x; }
            set { x = value; }
        }

        /// <summary>
        /// Y坐标
        /// </summary>
        public double Y
        {
            get { return y; }
            set { y = value; }
        }

        /// <summary>
        /// Z坐标
        /// </summary>
        public double Z
        {
            get { return z; }
            set { z = value; }
        }

        /// <summary>
        /// 默认构造函数，创建零向量
        /// </summary>
        public Vector3D()
        {
            x = 0;
            y = 0;
            z = 0;
        }

        /// <summary>
        /// 使用三个分量创建向量
        /// </summary>
        /// <param name="x">X坐标</param>
        /// <param name="y">Y坐标</param>
        /// <param name="z">Z坐标</param>
        public Vector3D(double x, double y, double z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        /// <summary>
        /// 复制构造函数
        /// </summary>
        /// <param name="other">要复制的向量</param>
        public Vector3D(Vector3D other)
        {
            this.x = other.x;
            this.y = other.y;
            this.z = other.z;
        }

        /// <summary>
        /// 计算向量长度
        /// </summary>
        /// <returns>向量长度</returns>
        public double Length()
        {
            return Math.Sqrt(x * x + y * y + z * z);
        }

        /// <summary>
        /// 计算向量长度的平方
        /// </summary>
        /// <returns>向量长度的平方</returns>
        public double LengthSquared()
        {
            return x * x + y * y + z * z;
        }

        /// <summary>
        /// 将向量归一化为单位长度
        /// </summary>
        /// <returns>归一化后的向量</returns>
        public Vector3D Normalize()
        {
            double length = Length();
            if (length < 1e-8)
            {
                return new Vector3D(0, 0, 0);
            }

            return new Vector3D(x / length, y / length, z / length);
        }

        /// <summary>
        /// 向量点乘
        /// </summary>
        /// <param name="other">另一个向量</param>
        /// <returns>点乘结果</returns>
        public double Dot(Vector3D other)
        {
            return x * other.x + y * other.y + z * other.z;
        }

        /// <summary>
        /// 向量叉乘
        /// </summary>
        /// <param name="other">另一个向量</param>
        /// <returns>叉乘结果</returns>
        public Vector3D Cross(Vector3D other)
        {
            return new Vector3D(
                y * other.z - z * other.y,
                z * other.x - x * other.z,
                x * other.y - y * other.x
            );
        }

        /// <summary>
        /// 静态点乘方法
        /// </summary>
        /// <param name="a">第一个向量</param>
        /// <param name="b">第二个向量</param>
        /// <returns>点乘结果</returns>
        public static double Dot(Vector3D a, Vector3D b)
        {
            return a.x * b.x + a.y * b.y + a.z * b.z;
        }

        /// <summary>
        /// 静态叉乘方法
        /// </summary>
        /// <param name="a">第一个向量</param>
        /// <param name="b">第二个向量</param>
        /// <returns>叉乘结果</returns>
        public static Vector3D Cross(Vector3D a, Vector3D b)
        {
            return new Vector3D(
                a.y * b.z - a.z * b.y,
                a.z * b.x - a.x * b.z,
                a.x * b.y - a.y * b.x
            );
        }

        /// <summary>
        /// 向量加法
        /// </summary>
        /// <param name="a">第一个向量</param>
        /// <param name="b">第二个向量</param>
        /// <returns>向量和</returns>
        public static Vector3D operator +(Vector3D a, Vector3D b)
        {
            return new Vector3D(a.x + b.x, a.y + b.y, a.z + b.z);
        }

        /// <summary>
        /// 向量减法
        /// </summary>
        /// <param name="a">第一个向量</param>
        /// <param name="b">第二个向量</param>
        /// <returns>向量差</returns>
        public static Vector3D operator -(Vector3D a, Vector3D b)
        {
            return new Vector3D(a.x - b.x, a.y - b.y, a.z - b.z);
        }

        /// <summary>
        /// 向量乘以标量
        /// </summary>
        /// <param name="vector">向量</param>
        /// <param name="scalar">标量</param>
        /// <returns>标量积</returns>
        public static Vector3D operator *(Vector3D vector, double scalar)
        {
            return new Vector3D(vector.x * scalar, vector.y * scalar, vector.z * scalar);
        }

        /// <summary>
        /// 标量乘以向量
        /// </summary>
        /// <param name="scalar">标量</param>
        /// <param name="vector">向量</param>
        /// <returns>标量积</returns>
        public static Vector3D operator *(double scalar, Vector3D vector)
        {
            return vector * scalar;
        }

        /// <summary>
        /// 向量除以标量
        /// </summary>
        /// <param name="vector">向量</param>
        /// <param name="scalar">标量</param>
        /// <returns>商</returns>
        public static Vector3D operator /(Vector3D vector, double scalar)
        {
            if (Math.Abs(scalar) < 1e-8)
            {
                throw new DivideByZeroException("除数不能为零");
            }

            double invScalar = 1.0 / scalar;
            return new Vector3D(vector.x * invScalar, vector.y * invScalar, vector.z * invScalar);
        }

        /// <summary>
        /// 向量取反
        /// </summary>
        /// <param name="vector">输入向量</param>
        /// <returns>取反后的向量</returns>
        public static Vector3D operator -(Vector3D vector)
        {
            return new Vector3D(-vector.x, -vector.y, -vector.z);
        }

        /// <summary>
        /// 计算两个向量之间的距离
        /// </summary>
        /// <param name="a">第一个向量</param>
        /// <param name="b">第二个向量</param>
        /// <returns>距离</returns>
        public static double Distance(Vector3D a, Vector3D b)
        {
            double dx = a.x - b.x;
            double dy = a.y - b.y;
            double dz = a.z - b.z;
            return Math.Sqrt(dx * dx + dy * dy + dz * dz);
        }

        /// <summary>
        /// 计算两个向量之间的距离平方
        /// </summary>
        /// <param name="a">第一个向量</param>
        /// <param name="b">第二个向量</param>
        /// <returns>距离平方</returns>
        public static double DistanceSquared(Vector3D a, Vector3D b)
        {
            double dx = a.x - b.x;
            double dy = a.y - b.y;
            double dz = a.z - b.z;
            return dx * dx + dy * dy + dz * dz;
        }

        /// <summary>
        /// 计算向量的平均值
        /// </summary>
        /// <param name="vectors">向量数组</param>
        /// <returns>平均向量</returns>
        public static Vector3D Average(params Vector3D[] vectors)
        {
            if (vectors == null || vectors.Length == 0)
            {
                return new Vector3D();
            }

            Vector3D sum = new Vector3D();
            foreach (var vector in vectors)
            {
                sum += vector;
            }

            return sum / vectors.Length;
        }

        /// <summary>
        /// 将向量转换为字符串
        /// </summary>
        /// <returns>字符串表示</returns>
        public override string ToString()
        {
            return $"({x:F4}, {y:F4}, {z:F4})";
        }

        /// <summary>
        /// 判断两个向量是否相等
        /// </summary>
        /// <param name="obj">要比较的对象</param>
        /// <returns>是否相等</returns>
        public override bool Equals(object obj)
        {
            if (obj is Vector3D other)
            {
                double epsilon = 1e-8;
                return Math.Abs(x - other.x) < epsilon && 
                       Math.Abs(y - other.y) < epsilon &&
                       Math.Abs(z - other.z) < epsilon;
            }
            return false;
        }

        /// <summary>
        /// 获取向量的哈希码
        /// </summary>
        /// <returns>哈希码</returns>
        public override int GetHashCode()
        {
            return x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode();
        }
    }
} 